﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;

using RpgLibrary;
using RpgLibrary.CharacterClasses;
using RpgLibrary.ItemClasses;
using RpgLibrary.SkillClasses;

namespace RpgEditor
{
    public partial class FormMain : Form
    {
        static bool isDirty = false;

        #region Manager Field Region

        protected static ItemDataManager itemManager;
        protected static CharacterDataManager entityDataManager;
        protected static SkillDataManager skillManager;

        #endregion

        #region Manager Property Region

        public static ItemDataManager ItemManager
        {
            get 
            {
                if (itemManager == null)
                    itemManager = new ItemDataManager();

                return itemManager; 
            }
            private set { itemManager = value; }
        }

        public static CharacterDataManager EntityDataManager
        {
            get 
            {
                if (entityDataManager == null)
                    entityDataManager = new CharacterDataManager();

                return entityDataManager; 
            }
            private set { entityDataManager = value; }
        }

        public static SkillDataManager SkillManager
        {
            get 
            {
                if (skillManager == null)
                    skillManager = new SkillDataManager();

                return skillManager; 
            }
            private set { skillManager = value; }
        }

        #endregion

        #region Field Region
        
        RolePlayingGame rolePlayingGame;
        FormDataList<FormEntityDetails, CharacterData> _frmClasses;
        FormDataList<FormArmorDetails, ArmorData> _frmArmor;
        FormDataList<FormShieldDetails, ShieldData> _frmShield;
        FormDataList<FormWeaponDetails, WeaponData> _frmWeapon;
        FormDataList<FormKeyDetails, KeyData> _frmKey;
        FormDataList<FormChestDetails, ChestData> _frmChest;
        FormDataList<FormSkillDetails, SkillData> _frmSkill;

        #endregion
        
        #region Property Region

        private FormDataList<FormEntityDetails, CharacterData> frmClasses
        {
            get
            {
                if (_frmClasses == null)
                    _frmClasses = InstantiateForm<FormEntityDetails, CharacterData>(
                        FormMain.EntityDataManager.CharacterData, RpgConstants.ClassPath);

                return _frmClasses;
            }
        }

        private FormDataList<FormChestDetails, ChestData> frmChest
        {
            get
            {
                if (_frmChest == null)
                    _frmChest = InstantiateForm<FormChestDetails, ChestData>(
                        FormMain.ItemManager.ChestData, RpgConstants.ChestPath);

                return _frmChest;
            }
        }

        private FormDataList<FormSkillDetails, SkillData> frmSkill
        {
            get
            {
                if (_frmSkill == null)
                    _frmSkill = InstantiateForm<FormSkillDetails, SkillData>(
                        FormMain.SkillManager.SkillData, RpgConstants.SkillPath);

                return _frmSkill;
            }
        }

        private FormDataList<FormArmorDetails, ArmorData> frmArmor
        {
            get
            {
                if (_frmArmor == null)
                    _frmArmor = InstantiateForm<FormArmorDetails, ArmorData>(
                        FormMain.ItemManager.ArmorData, RpgConstants.ArmorPath);

                return _frmArmor;
            }
        }

        private FormDataList<FormShieldDetails, ShieldData> frmShield
        {
            get
            {
                if (_frmShield == null)
                    _frmShield = InstantiateForm<FormShieldDetails, ShieldData>(
                        FormMain.ItemManager.ShieldData, RpgConstants.ShieldPath);

                return _frmShield;
            }
        }

        private FormDataList<FormWeaponDetails, WeaponData> frmWeapon
        {
            get
            {
                if (_frmWeapon == null)
                    _frmWeapon = InstantiateForm<FormWeaponDetails, WeaponData>(
                        FormMain.ItemManager.WeaponData, RpgConstants.WeaponPath);

                return _frmWeapon;
            }
        }

        private FormDataList<FormKeyDetails, KeyData> frmKey
        {
            get
            {
                if (_frmKey == null)
                    _frmKey = InstantiateForm<FormKeyDetails, KeyData>(
                        FormMain.ItemManager.KeyData, RpgConstants.KeyPath);

                return _frmKey;
            }
        }

        #endregion
        
        #region Constructor Region
        
        public FormMain()
        {
            InitializeComponent();

            this.FormClosing += new FormClosingEventHandler(FormMain_FormClosing);

            newGameToolStripMenuItem.Click += new EventHandler(newGameToolStripMenuItem_Click);
            openGameToolStripMenuItem.Click += new EventHandler(openGameToolStripMenuItem_Click);
            saveGameToolStripMenuItem.Click += new EventHandler(saveGameToolStripMenuItem_Click);
            exitToolStripMenuItem.Click += new EventHandler(exitToolStripMenuItem_Click);
            
            classesToolStripMenuItem.Click += new EventHandler(classesToolStripMenuItem_Click);
            armorToolStripMenuItem.Click += new EventHandler(armorToolStripMenuItem_Click);
            shieldToolStripMenuItem.Click += new EventHandler(shieldToolStripMenuItem_Click);
            weaponsToolStripMenuItem.Click += new EventHandler(weaponsToolStripMenuItem_Click);

            keysToolStripMenuItem.Click += new EventHandler(keysToolStripMenuItem_Click);
            chestsToolStripMenuItem.Click += new EventHandler(chestsToolStripMenuItem_Click);

            skillsToolStripMenuItem.Click += new EventHandler(skillsToolStripMenuItem_Click);
        }
        #endregion
        
        #region Menu Item Event Handler Region
        
        void newGameToolStripMenuItem_Click(object sender, EventArgs e)
        {
            using (FormNewGame frmNewGame = new FormNewGame())
            {
                DialogResult result = frmNewGame.ShowDialog();

                if (result == DialogResult.OK && frmNewGame.RolePlayingGame != null)
                {
                    FolderBrowserDialog folderDialog = new FolderBrowserDialog();
                    folderDialog.Description = "Select folder to create game in";
                    folderDialog.SelectedPath = Application.StartupPath;

                    DialogResult folderResult = folderDialog.ShowDialog();

                    if (folderResult == DialogResult.OK)
                    {
                        try
                        {
                            RpgConstants.BasePath = folderDialog.SelectedPath;

                            if (Directory.Exists(RpgConstants.GamePath))
                                throw new Exception("Selected directory already exists.");

                            Directory.CreateDirectory(RpgConstants.GamePath);
                            Directory.CreateDirectory(RpgConstants.ClassPath);
                            Directory.CreateDirectory(RpgConstants.ArmorPath);
                            Directory.CreateDirectory(RpgConstants.ShieldPath);
                            Directory.CreateDirectory(RpgConstants.WeaponPath);
                            Directory.CreateDirectory(RpgConstants.KeyPath);
                            Directory.CreateDirectory(RpgConstants.ChestPath);
                            Directory.CreateDirectory(RpgConstants.SkillPath);
                            
                            rolePlayingGame = frmNewGame.RolePlayingGame;

                            XnaSerializer.Serialize<RolePlayingGame>(RpgConstants.GameFilePath, rolePlayingGame);
                        }
                        catch (Exception ex)
                        {
                            MessageBox.Show(ex.ToString());
                            return;
                        }

                        ActivateMenu();
                    }
                }
            }
        }

        void openGameToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog folderDialog = new FolderBrowserDialog();
            
            folderDialog.Description = "Select Game folder";
            folderDialog.SelectedPath = String.IsNullOrEmpty(Program.PreviouslyLoadedGamePath) 
                ? Application.StartupPath
                : Program.PreviouslyLoadedGamePath;
            
            bool tryAgain = false;
            
            do
            {
                tryAgain = false;
                DialogResult folderResult = folderDialog.ShowDialog();
                DialogResult msgBoxResult;
                if (folderResult == DialogResult.OK)
                {
                    if (File.Exists(Path.Combine(folderDialog.SelectedPath, "Game","Game.xml")))
                    {
                        //try
                        {
                            OpenGame(folderDialog.SelectedPath);
                            tryAgain = false;
                        }
                        //catch (Exception ex)
                        //{
                        //    msgBoxResult = MessageBox.Show(
                        //        ex.ToString(),
                        //        "Error opening game.",
                        //        MessageBoxButtons.RetryCancel);
                            
                        //    if (msgBoxResult == DialogResult.Cancel)
                        //        tryAgain = false;
                        //    else if (msgBoxResult == DialogResult.Retry)
                        //        tryAgain = true;
                        //}
                    }
                    else
                    {
                        msgBoxResult = MessageBox.Show(
                            "Game not found, try again?",
                            "Game does not exist",
                            MessageBoxButtons.RetryCancel);
                        
                        if (msgBoxResult == DialogResult.Cancel)
                            tryAgain = false;

                        else if (msgBoxResult == DialogResult.Retry)
                            tryAgain = true;
                    }
                }
            } while (tryAgain);
        }

        private void OpenGame (string path)
        {
            RpgConstants.BasePath = path;

            rolePlayingGame = XnaSerializer.Deserialize<RolePlayingGame>(RpgConstants.GameFilePath);
                
            FormMain.ReadAllData();

            ActivateMenu();

            Program.PreviouslyLoadedGamePath = path;
        }

       

        FormDataList<DetailFormType, DataObjectType> InstantiateForm<DetailFormType, DataObjectType>(
            Dictionary<string, DataObjectType> dataDictionary, 
            string dataObjectPath)
            where DetailFormType : IDisposable, IDetailForm, new()
            where DataObjectType : IRpgDataObject
        {
            FormDataList<DetailFormType, DataObjectType> frm = new FormDataList<DetailFormType, DataObjectType>(dataDictionary, dataObjectPath);
            frm.MdiParent = this;

            return frm;
        }

        private void ActivateMenu()
        {
            foreach (ToolStripItem item in menuStrip1.Items)
                item.Enabled = true;
        }

        void saveGameToolStripMenuItem_Click(object sender, EventArgs e)
        {
            SaveGame();
        }

        private void SaveGame()
        {
            if (rolePlayingGame != null)
            {
                WriteAllData();
            }
        }

        void exitToolStripMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        void ShowDataListForm(Form frm)
        {
            frm.Show();
            frm.BringToFront();
        }

        void classesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowDataListForm(frmClasses);
        }

        void armorToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowDataListForm(frmArmor);
        }

        void shieldToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowDataListForm(frmShield);
        }

        void weaponsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowDataListForm(frmWeapon);
        }

        void chestsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowDataListForm(frmChest);
        }

        void keysToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowDataListForm(frmKey);
        }

        void skillsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ShowDataListForm(frmSkill);
        }

        void FormMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (FormMain.isDirty)
            {
                //Instead of this I should just ask if the user wants to save before exiting
                DialogResult result = MessageBox.Show(
                    "Do you want to save before exiting?",
                    "Save befor exit?",
                    MessageBoxButtons.YesNo,
                    MessageBoxIcon.Warning);

                if (result == DialogResult.Yes)
                    SaveGame();
            }
        }

        #endregion

        private void FormMain_Load(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(Program.PreviouslyLoadedGamePath))
                if (Directory.Exists(Program.PreviouslyLoadedGamePath))
                    OpenGame(Program.PreviouslyLoadedGamePath);
        }
        
        #region Method Region

        private void WriteAllData()
        {
            try
            {
                XnaSerializer.Serialize<RolePlayingGame>(RpgConstants.GameFilePath, rolePlayingGame);

                WriteObjectData<CharacterData>(
                    FormMain.EntityDataManager.CharacterData, RpgConstants.ClassPath);

                WriteObjectData<ArmorData>(
                    FormMain.ItemManager.ArmorData, RpgConstants.ArmorPath);

                WriteObjectData<ShieldData>(
                    FormMain.ItemManager.ShieldData, RpgConstants.ShieldPath);

                WriteObjectData<WeaponData>(
                    FormMain.ItemManager.WeaponData, RpgConstants.WeaponPath);

                WriteObjectData<KeyData>(
                    FormMain.ItemManager.KeyData, RpgConstants.KeyPath);

                WriteObjectData<ChestData>(
                    FormMain.ItemManager.ChestData, RpgConstants.ChestPath);

                WriteObjectData<SkillData>(
                    FormMain.SkillManager.SkillData, RpgConstants.SkillPath);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Error saving game.");
            }
        }

        public static void ReadAllData()
        {
            FormMain.ReadObjectData<CharacterData>(
                EntityDataManager.CharacterData, RpgConstants.ClassPath);

            FormMain.ReadObjectData<ArmorData>(
                ItemManager.ArmorData, RpgConstants.ArmorPath);

            FormMain.ReadObjectData<ShieldData>(
                ItemManager.ShieldData, RpgConstants.ShieldPath);

            FormMain.ReadObjectData<WeaponData>(
                ItemManager.WeaponData, RpgConstants.WeaponPath);

            FormMain.ReadObjectData<KeyData>(
                ItemManager.KeyData, RpgConstants.KeyPath);

            FormMain.ReadObjectData<ChestData>(
                ItemManager.ChestData, RpgConstants.ChestPath);

            FormMain.ReadObjectData<SkillData>(
                SkillManager.SkillData, RpgConstants.SkillPath);
        }

        public static void ReadObjectData<DataObjectType>(Dictionary<string,DataObjectType> dataDictionary, string dataObjectPath)
            where DataObjectType : IRpgDataObject
        {
            string[] fileNames = Directory.GetFiles(
                dataObjectPath, "*.xml");

            foreach (string dataObjectName in fileNames)
            {
                DataObjectType objectData = XnaSerializer.Deserialize<DataObjectType>(dataObjectName);
                dataDictionary.Add(objectData.Name, objectData);
            }
        }


        public static void WriteObjectData<DataObjectType>(Dictionary<string, DataObjectType> dataDictionary, string dataObjectPath)
            where DataObjectType : IRpgDataObject
        {
            foreach (string dataObjectName in dataDictionary.Keys)
            {
                XnaSerializer.Serialize<DataObjectType>(
                    Path.Combine(dataObjectPath, dataObjectName + ".xml"),
                    dataDictionary[dataObjectName]);
            }
        }

        #endregion
    }
}